#pragma once
#include "InetAddr.hpp"
#include "Log.hpp"
#include "Socket.hpp"
#include <sys/epoll.h>

using namespace LogModule;
using namespace InetAddrModule;
using namespace SocketModule;

const int NUM = 40;
const int gdefaultfd = -1;

class EpollSever
{
private:
    void Accepter()
    {
        InetAddr peer;
        int newfd = _listen_socketfd->Accepter(&peer);
        if (newfd < 0)
            return;

        LOG(LogLevel::DEBUG) << "获得一个新连接：" << peer.Addr();

        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = newfd;

        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, newfd, &ev);
        if (n < 0)
        {
            LOG(LogLevel::ERROR) << "epoll_ctl error";
            close(newfd);
        }
        LOG(LogLevel::DEBUG) << "epoll_ctl success: " << newfd;
    }
    void Recver(int fd)
    {
        char buffer[1024];
        ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            LOG(LogLevel::DEBUG) << "client# " << buffer;

            std::string message = "echo# ";
            message += buffer;
            send(fd, message.c_str(), message.size(), 0);
        }
        else if (n == 0)
        {
            LOG(LogLevel::DEBUG) << "客户端退出, sockfd: " << fd;
            int m = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            if (m < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                return;
            }
            close(fd);
        }
        else
        {
            LOG(LogLevel::DEBUG) << "客户端读取出错, sockfd: " << fd;
            int m = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            if (m < 0)
            {
                LOG(LogLevel::ERROR) << "epoll_ctl error";
                return;
            }
            close(fd);
        }
    }
    void Dispatcher(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int events = _fds[i].events;
            int fd = _fds[i].data.fd;

            if (fd == gdefaultfd)
                continue;
            // 两种情况：listen socket 或者 读文件描述符 就绪
            if (fd == _listen_socketfd->Fd())
            {
                if (events & EPOLLIN)
                {
                    Accepter();
                }
            }
            else
            {
                if (events & EPOLLIN)
                {
                    Recver(fd);
                }
            }
        }
    }

public:
    EpollSever(uint16_t port) : _port(port),
                                _listen_socketfd(std::make_unique<TcpSocket>()),
                                _epfd(gdefaultfd),
                                _running(false)
    {
    }
    void Init()
    {
        _listen_socketfd->BuildTcpSocketMethod(_port);
        _epfd = ::epoll_create(100);
        if (_epfd < 0)
        {
            LOG(LogLevel::ERROR) << "epoll_create error";
            exit(10);
        }
        LOG(LogLevel::DEBUG) << "epoll_create success: " << _epfd;

        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = _listen_socketfd->Fd();

        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listen_socketfd->Fd(), &ev);
        if (n < 0)
        {
            LOG(LogLevel::ERROR) << "epoll_ctl error";
            exit(20);
        }
    }
    void Loop()
    {
        _running = true;

        int timeout = -1; // 单位：毫秒
        while (_running)
        {
            int n = ::epoll_wait(_epfd, _fds, NUM, timeout);

            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "select failed";
                break;
            }
            else if (n == 0)
            {
                LOG(LogLevel::ERROR) << "数据未就绪, timeout……";
            }
            else
            {
                LOG(LogLevel::DEBUG) << "事件已就绪……";
                Dispatcher(n);
            }
            sleep(1);
        }
        _running = false;
    }
    ~EpollSever()
    {
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listen_socketfd;
    int _epfd;
    bool _running;
    struct epoll_event _fds[NUM];
};
